package com.wdciti.sccp.iot.springvertx.protocols.tcp;

import com.wdciti.sccp.iot.springvertx.EndPointVerticle;
import com.wdciti.sccp.iot.springvertx.protocols.tcp.ext.EndpointHeadersAdaptor;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.net.NetSocket;
import lombok.extern.slf4j.Slf4j;

import java.time.Instant;

/**
 * @author by early
 * @blame IOT Team
 * @date 2022/04/22.
 */
@Slf4j
public class NetHandler implements Handler<NetSocket> {

    private final String codecName;
    private final String busAddr;
    private final EventBus eventBus;

    private final boolean hexDumpEnable;

    public NetHandler(EventBus eventBus, String busAddr, String codecName, boolean hexDumpEnable) {
        this.busAddr = busAddr;
        this.eventBus = eventBus;
        this.codecName = codecName;
        this.hexDumpEnable = hexDumpEnable;
    }

    @Override
    public void handle(NetSocket socket) {

        log.info("#TCP-CONNECT# --server:[{}] remote:[{}]! whId={}", socket.localAddress(), socket.remoteAddress(), socket.writeHandlerID());
        //关闭钩子。
        socket.closeHandler(close -> log.info("CLOSED! --tcp Connection[id={}] closed on Server[{}]!", socket.writeHandlerID(), socket.remoteAddress()));
        //异常处理
        socket.exceptionHandler(Throwable::printStackTrace);
        //将socket数据包转发给eventBus
        socket.handler(buf -> {
            if (buf != null && buf.getBytes().length != 0) {
                MultiMap map = EndpointHeadersAdaptor.emptyHeaders()
                        .addBusAddr(this.busAddr, EndPointVerticle.IN)
                        .addLocalAddress(socket.localAddress())
                        .addRemoteAddress(socket.remoteAddress())
                        .addRorS(EndpointHeadersAdaptor.RS.R)
                        .addBusQob(EndpointHeadersAdaptor.QOB.SEND)
                        .headers();
                DeliveryOptions opt = new DeliveryOptions().setHeaders(map).setCodecName(codecName);
                if (hexDumpEnable) {
                    EndpointHeadersAdaptor adaptor = EndpointHeadersAdaptor.instanceFrom(map);
                    log.info(printHex(adaptor, buf));
//                eventBus.send(DUMP_BUS_ADDR, buf, opt);
                }
                eventBus.send(this.busAddr + EndPointVerticle.IN, buf, opt);
            }
        });

        //监听eventBus消息，转投给socket
        eventBus.<Buffer>consumer(this.busAddr + EndPointVerticle.OUT).handler(msg -> {
            DeliveryOptions dopt = new DeliveryOptions().setCodecName(codecName);
            EndpointHeadersAdaptor adaptor = EndpointHeadersAdaptor.instanceFrom(msg.headers());
            adaptor.addRemoteAddress(socket.remoteAddress())
                    .addLocalAddress(socket.localAddress())
                    .addRorS(EndpointHeadersAdaptor.RS.S)
                    .addBusAddr(this.busAddr, EndPointVerticle.OUT)
                    .addBusQob(EndpointHeadersAdaptor.QOB.PUBLISH);
            dopt.setHeaders(adaptor.headers());
            if (hexDumpEnable) {
                log.info(printHex(adaptor, msg.body()));
            }
            socket.write(msg.body());
            if (socket.writeQueueFull()) {
                socket.pause().drainHandler(done -> socket.resume());
            }
        });
    }


    private String printHex(EndpointHeadersAdaptor adaptor, Buffer body) {
        String id = adaptor.getBusAddr().replace(EndPointVerticle.PROTOCOL, "") + "(" + adaptor.getBusAddrTail() + ")";
        String remote = "[" + adaptor.getRemoteAddress() + "]";
        String local = "[" + adaptor.getLocalAddress() + "]";
        ByteBuf buffer = body.getByteBuf();
        String arrow = "---";
        if (adaptor.getRorS().equals(EndpointHeadersAdaptor.RS.R)) {
            arrow = "--->";
        } else if (adaptor.getRorS().equals(EndpointHeadersAdaptor.RS.S)) {
            arrow = "<---";
        }
        return id + "   " + remote + arrow + local + "..." + Instant.now() + "..." + "\r\n" + ByteBufUtil.prettyHexDump(buffer);
    }
}
